﻿using System;
using System.Collections.Specialized;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Moq;

namespace CodeContrib.Web.Mvc
{
	public static class MoqExtensions
	{
		public static HttpContextBase CreateMockHttpContext()
		{
			var mockRequest = new Mock<HttpRequestBase>();
			var mockResponse = new Mock<HttpResponseBase>();
			var mockServer = new Mock<HttpServerUtilityBase>();
			var mockSession = new Mock<HttpSessionStateBase>();

			var mockContext = new Mock<HttpContextBase>();
			mockContext.Setup(x => x.Request).Returns(mockRequest.Object);
			mockContext.Setup(x => x.Response).Returns(mockResponse.Object);
			mockContext.Setup(x => x.Server).Returns(mockServer.Object);
			mockContext.Setup(x => x.Session).Returns(mockSession.Object);

			return mockContext.Object;
		}

		public static HttpContextBase CreateMockHttpContext(string url)
		{
			Guard.NotNullOrEmpty(url, "url");

			var context = CreateMockHttpContext();
			context.Request.SetRequestUrl(url);

			return context;
		}

		private static NameValueCollection GetQueryStringParameters(string url)
		{
			Guard.NotNullOrEmpty(url, "url");

			if (!url.Contains("?"))
			{
				return null;
			}

			var parameters = new NameValueCollection();

			var segments = url.Split("?".ToCharArray());
			var keys = segments[1].Split("&".ToCharArray());

			foreach (var part in keys.Select(key => key.Split("=".ToCharArray())))
			{
				parameters.Add(part[0], part[1]);
			}

			return parameters;
		}

		private static string GetUrlFileName(string url)
		{
			Guard.NotNullOrEmpty(url, "url");

			return url.Contains("?") ? url.Substring(0, url.IndexOf("?")) : url;
		}

		public static void SetAjaxRequest(this HttpRequestBase mocked)
		{
			var request = Mock.Get(mocked);
			request.Setup(x => x["X-Requested-With"]).Returns("XMLHttpRequest");
		}

		public static void SetHttpMethod(this HttpRequestBase request, string httpMethod)
		{
			Guard.NotNullOrEmpty(httpMethod, "httpMethod");

			Mock.Get(request).Setup(x => x.HttpMethod).Returns(httpMethod);
		}

		public static void SetMocks(this Controller controller)
		{
			var httpContext = CreateMockHttpContext();

			var requestContext = new RequestContext(httpContext, new RouteData());

			var urlHelper = new UrlHelper(requestContext);

			controller.ControllerContext = new ControllerContext(requestContext, controller);
			controller.Url = urlHelper;
		}

		public static void SetRequestUrl(this HttpRequestBase mocked, string url)
		{
			Guard.NotNullOrEmpty(url, "url");

			if (!url.StartsWith("~/"))
			{
				throw new ArgumentException("The url should start with \"~/\".");
			}

			var request = Mock.Get(mocked);
			request.Setup(x => x.QueryString).Returns(GetQueryStringParameters(url));
			request.Setup(x => x.AppRelativeCurrentExecutionFilePath).Returns(GetUrlFileName(url));
			request.Setup(x => x.PathInfo).Returns(string.Empty);
		}
	}
}